<?php

namespace App\Controllers\Notify;

use App\Libs\DB\PailePdo;
use \App\Libs\Pay\PayLibs;
use \App\Models\Order\Order;
use \App\Models\Order\PailebaoOrder;
use App\Libs\Log\Logger;
use App\Models\User\User;
use \App\Libs\Base\Config;
use App\Libs\Helper\WeixinHelper;
use App\Models\Goods\Goods;
use App\Models\Store\Store;
class NotifyAll extends \App\Libs\Base\BaseAction
{
    protected $returnData;
    protected function beforeRun()
    {
        parent::beforeRun();
//        $this->needLogin = TRUE;
    }


    /*
     * 支付日志记录：在app/logs文件夹下；
     * 每次微信支付的结果通知，支付宝支付的结果通知都会记录,并且会按天存放；
     * weixinnotify+INFO+日期 ：存放微信，  alipaynotify+INFO.日期:存放支付宝;
     *
     * 出现一下错误时会记录日志：
     * 1：用户在支付宝或者微信，支付成功，但系统修改本地数据库中相应订单状态时失败会记录日志：
     * 微信：
     *          weixinBuy-SuccessLocalError +INFO.日期  :用户使用微信购买东西，修改数据库失败
     *          weixinCharge-SuccessLocalError +INFO.日期 :用户使用支付宝充值派乐宝，修改本地数据库失败
     * 支付宝：
     *          alipayBuy-SuccessLocalError +INFO.日期
     *          alipayCharge-SuccessLocalError +INFO.日期
     * 异常排除建议：
     *          buy：仅涉及到订单状态的修改
     *          charge：涉及到充值派乐宝的流水表的状态，信息等，以及用户信息中的余额;
     * */
    public function execute()
    {

        $postStr = file_get_contents('php://input');
        //转换成xml;如果是支付宝的返回，转换后是false；，
        $postObj = @simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);

        if (is_object($postObj)) {
           $this->returnData = self::xmlToArray($postObj);
            //-------------------记录每笔微信支付的信息；-------------------------- //|
           self::logOrderInfo('微信',$this->returnData,'weixinnotify');
            //------------------------------------------------------------  //|
            if ($this->returnData['return_code'] === 'SUCCESS') {
                $this->weixinNotify();
            }
            $ret = array('return_code' => 'SUCCESS', 'return_msg' => 'OK');
            $xmlResponse = WeixinHelper::arrayToXml($ret);
            echo $xmlResponse;
            exit;
        } else {
            //支付宝回调；
            $post = $this->request->getPost();
            //-------------------记录每笔支付宝支付的信息；-------------------------- //|
            self::logOrderInfo('支付宝',$post,'alipaynotify');
            //------------------------------------------------------------  //|
            $alipay = new PayLibs(1, true);
            //验证是否是！！支付宝！！发给我的
            $flag = $alipay->alipay_notify($post);
            if ($flag) {
                //验证支付逻辑
                $out_trade_no = $post['out_trade_no'];
		        $alipay_trade_no = $post['trade_no'];
		        $alipay_pay_time = $post['gmt_payment'];
                //交易状态：字符串
                $trade_status = $post['trade_status'];
                //交易额
                $total_amount = $post['total_amount'];
                $pass_params = json_decode(urldecode($post['passback_params']));
                $appId = $post['app_id'];
                //1 ：验证appid
                $alipayInfo = Config::instance()->pay['alipay'];
                if ($alipayInfo['appId'] != $appId) {
                    echo 'failure';
                    die;
                }
                //2 ： 验证seller_email
                if ($alipayInfo['seller_email'] != $post['seller_email']) {
                    echo 'failure';
                    die;
                }

                //3 : 验证是否有此订单号码,分为：charge，充值派乐宝，买东西，buy
                if ($pass_params->type == 'charge') {
                    $matchOrder = PailebaoOrder::find([
                        'conditions' => 'order_sn=?1 and operate=1 and pay_method=2',
                        'bind' => [1 => $out_trade_no]
                    ]);
                    if (empty($matchOrder)) {
                        echo 'failure';
                        die;
                    }
                    //4 : 验证订单金额是否是正确的
                    if ($matchOrder->money_num != $total_amount) {
                        echo 'failure';
                        die;
                    }
                } elseif ($pass_params->type == 'buy') {
                    $matchOrder = Order::find([
                        'conditions' => 'order_sn=?1 and pay_method=2',
                        'bind' => [1 => $out_trade_no]
                    ]);
                    if (empty($matchOrder)) {
                        echo 'failure';
                        die;
                    }
                    //4 : 验证订单金额是否是正确的
                    if ($matchOrder->price != $total_amount) {
                        echo 'failure';
                        die;
                    }
                }

                //官方文档4步验证完成，所有数据校验已经完成，以下所有输出，都应该返回成功，而不是失败;
                //确定交易状态，更改订单状态
                if ($trade_status == 'TRADE_SUCCESS' || $trade_status == 'TRADE_FINISHED') {

//             是否已支付 0:待支付  1:已支付 2:已取消 3.已过期 4.已失效 5.已使用 6.已退款 9.异常
                    //外部验证完了，内部验证开始;
                    if ($pass_params->type == 'buy') {
                        //对于买东西的订单表
                        //验证订单状态，如果订单状态不是待支付0，返回failure
                        if ($matchOrder->status != 0) {
                            //可能是重复通知，支付宝的通知间隔较长，此处运行几率较小;
                            echo 'success';
                            die;
                        }
                        //状态，购买信息，支付方式，外部订单号；
                        $matchOrder->status = 1;
                        $matchOrder->buy_info = json_encode($post);
                        $matchOrder->out_trade_sn = $alipay_trade_no;
//                        $matchOrder->ptime = date('Y-m-d H:i:s');
                        $matchOrder->ptime = $alipay_pay_time;
                        $res = $matchOrder->update();
                        //增加销量
                        Goods::addSaleNumber($matchOrder->gid);
                        if ($res) {
                            echo 'success';
                            die;
                        }else{
                            //用户支付成功了，但是修改本地数据库失败了；
                            echo 'success';
                            self::paySuccessLocalError('支付宝购买','支付宝','用户付款成功，修改本地数据库失败',$post,'alipayBuy-SuccessLocalError');
                        }
                    } elseif ($pass_params->type == 'charge') {
                        if ($matchOrder->order_status != 0) {
                            echo 'success';
                            die;
                        }
                        //验证金额；前边已经验证过了

                        //流水表状态；加钱；
                        PailePdo::begin();
                        $matchOrder->order_status = '1';
                        $matchOrder->operate_info = json_encode($post);
                        $matchOrder->out_order_sn = $alipay_trade_no;
                        if ($matchOrder->update()) {
                            $matchUser = User::find([
                                'conditions' => 'id=?1',
                                'bind' => [1 => $matchOrder->uid]
                            ]);
                            $matchUser->balance = (float)$matchUser->balance + (float)$total_amount;
                            //冲300，送会员；
                            if($total_amount>300 && $matchUser->vip =='0'){
                                $matchUser->vip = '1';
                            }
                            if ($matchUser->update()) {
                                PailePdo::commit();
                            } else {
                                PailePdo::rollback();
                                //记录日志：内容：用户充值成功而来，但是修改流水表成功了，但是增加用户派乐宝余额时失败了
                                self::paySuccessLocalError('支付宝充值派乐宝','支付宝','用户付款成功，修改本地数据库失败',$post,'alipayCharge-SuccessLocalError');
                            }
                            echo 'success';  //总要echo success;
                            die;
                        } else {
                            PailePdo::rollback();//不是很需要；
                            self::paySuccessLocalError('支付宝充值派乐宝','支付宝','用户付款成功，修改本地数据库失败',$post,'alipayCharge-SuccessLocalError');
                            //内容：用户充值成功，但是修改流水表的订单状态时失败了，并且用户派乐宝金额未增加
                            echo 'success';
                            die;
                        }
                    }
                } elseif ($trade_status == 'TRADE_CLOSED') {
                    //买东西的在alipay创建的订单失效了，需要改变订单表的状态；
                    //支付宝方的订单状态失效了，不可能继续支付；
                    echo 'success';
                    die;
                }
            } else {
                //验签失败
                echo 'failure';
                die;
            }
        }
        die;
    }


    public function weixinNotify(){
        $sign = $this->returnData['sign'];
        $type = $this->returnData['attach'];
        $returnData = $this->returnData;
        unset($returnData['sign']);
//        unset($returnData['attach']);
        ksort($returnData);
        $str = http_build_query($returnData);
        $str .= '&key=' . \App\Libs\Helper\WeixinHelper::$key;
        $md5 = strtoupper(md5($str));
        if ($sign !== $md5) {
            return;
        }
        $totalmount = $this->returnData['total_fee']/100;  //单位元；
        switch ($type){
            case 'buy':
                $matchOrder = Order::find([
                    'conditions'  => 'order_sn=?1 and pay_method=1',
                    'bind' =>[1=>$returnData['out_trade_no']]
                ]);
                //验证金额；
                if ($matchOrder->price != $totalmount) {
                    //此处：用户支付金额与订单金额不符;
                    return;
                }
                //多次通知屏蔽;
                if ($matchOrder->status != 0) {
                    //可能是重复通知，或者是：订单状态被私自改变;
                    return;
                }
                //状态，购买信息，支付方式，外部订单号；
                $matchOrder->status = 1;
                $matchOrder->buy_info = json_encode($this->returnData);
                $matchOrder->out_trade_sn = $this->returnData['transaction_id'];  //尾部订单号：微信支付宝系统的；
                $matchOrder->ptime = date('Y-m-d H:i:s');
                //增加销量；
                Goods::addSaleNumber($matchOrder->gid);
                if(!$matchOrder->update()){
                    //有一单微信购买交易支付成功，但修改数据库失败，交易信息如下：
                    self::paySuccessLocalError('微信购买','微信支付','用户支付成功，更新本地订单状态失败',$this->returnData,'weixinBuy-SuccessLocalError');
                }
            break;
            case 'charge':
                $matchOrder = PailebaoOrder::find([
                    'conditions'  => 'order_sn=?1 and pay_method=1',
                    'bind' =>[1=>$returnData['out_trade_no']]
                ]);
               //验证金额：
                if ($matchOrder->money_num != $totalmount) {
                    return;
                }
                //屏蔽多次提示:或者如果状态改变是私自修改数据库的结果;
                if ($matchOrder->order_status != 0) {
                    return;
                }

                //流水表状态；加钱；
                PailePdo::begin();
                $matchOrder->order_status = '1';
                $matchOrder->operate_info = json_encode($this->returnData);
                $matchOrder->out_order_sn = $this->returnData['transaction_id'];
                if ($matchOrder->update()) {
                    $matchUser = User::find([
                        'conditions' => 'id=?1',
                        'bind' => [1 => $matchOrder->uid]
                    ]);
                    $matchUser->balance = (float)$matchUser->balance + (float)$totalmount;
                    //充值300元以上就给个会员;
                    if($totalmount>=300 && $matchUser->vip=='0'){
                        $matchUser->vip = '1';
                    }
                    if ($matchUser->update()) {
                        PailePdo::commit();
                    } else {
                        PailePdo::rollback();
                        //记录日志：内容：用户充值成功而来，但是修改流水表成功了，但是增加用户派乐宝余额时失败了
                        self::paySuccessLocalError('微信充值派乐宝','微信支付','用户付款成功，修改本地数据库失败',$this->returnData,'weixinCharge-SuccessLocalError');
                        return;
                    }
                } else {
                    PailePdo::rollback();//不是很需要；
                  self::paySuccessLocalError('微信充值派乐宝','微信支付','用户付款成功，修改本地数据库失败',$this->returnData,'weixinCharge-SuccessLocalError');
                    //内容：用户充值成功，但是修改流水表的订单状态时失败了，并且用户派乐宝金额未增加
                   return ;
                }
        }
    }

    //用于记录每次的通知信息；
    public static function logOrderInfo($payType,$data,$logFileName){

$msgbefore ="
########################################################
系统收到一笔{$payType}订单通知，详细信息如下：
";
$afterMsg='
########################################################
';
            $msg = $msgbefore . var_export($data, true).$afterMsg;
            Logger::info($logFileName, $msg);
    }

    public static function paySuccessLocalError($type,$paytype,$errorMsg,$data,$logFileName){
        $msgbefore ="
########################################################
类型：{$type}                支付方式：{$paytype}     
错误信息：{$errorMsg}  
";
        $afterMsg='
########################################################
';
        $msg = $msgbefore . var_export($data, true).$afterMsg;
        Logger::info($logFileName, $msg);
    }


    public static function xmlToArray($simpleXmlElement){
        $simpleXmlElement=(array)$simpleXmlElement;
        foreach($simpleXmlElement as $k=>$v){
            if($v instanceof SimpleXMLElement ||is_array($v)){
                $simpleXmlElement[$k]=xmlToArray($v);
            }
        }
        return $simpleXmlElement;
    }

}

